package vcp.server.requesthandler;

import ocsf.server.ConnectionToClient;

import org.joda.time.DateTime;
import org.joda.time.Minutes;

import vcp.client.request.CustomerCheckoutRequest;
import vcp.client.request.response.ResponseRequest;
import vcp.client.utility.Common.UserNotification;
import vcp.entity.Car;
import vcp.entity.Customer;
import vcp.entity.Lot;
import vcp.entity.Site;
import vcp.entity.Subscription;
import vcp.entity.SubscriptionHistory;
import vcp.entity.utility.Robot;
import vcp.entity.utility.RobotManager;
import vcp.enums.LotStatus;
import vcp.enums.SiteEnum;
import vcp.server.interfaces.IServer;
import vcp.server.utility.SystemClock;

/**
 * 
 * @author VCP team
 *         <p>
 *         class handles requests related to check out on server side
 *         </p>
 */
public class CustomerCheckoutRequestHandler extends RequestHandler<CustomerCheckoutRequest>
{
	/**
	 * class constructor
	 * 
	 * @param server
	 */
	public CustomerCheckoutRequestHandler(IServer server)
	{
		super(server);
	}

	/****************************************************************************************************************************
	 *** CommandHandler methods ***
	 ****************************************************************************************************************************/
	@Override
	public void execute(CustomerCheckoutRequest request, ConnectionToClient client)
	{
		ResponseRequest response = new ResponseRequest(request.getRequestID(), null);
		Car car = this.getDaoFactory().getCarDAO().findByID(request.getCarID());

		boolean success = false;
		if (this.isCarIDCorrect(car))
		{
			Lot lot = this.getDaoFactory().getLotDAO().findByCar(request.getCarID());
			Site site = lot.getParentSite();

			if (this.isCarIDNotRegisteredToSite(request, lot, site))
			{
				success = true;
				this.removeOccupiedLotFromSite(lot, site);

				site = this.rebuildSiteParkingMatrix(request, lot);

				// calculate time from start until now
				DateTime currentTime = SystemClock.getClockInstance().getSystemDateTime();
				int useHours = this.calculateHours(car, currentTime);

				Subscription subscription = this.getDaoFactory().getSubscriptionDAO().findByID(car.getSubscriptionID());
				switch (subscription.getSubscriptionType())
				{
					case CASUAL_SUBSCRIBER:
						this.calculateCasual(response, car, currentTime);
						break;
					case FULL_SUBSCRIBER:
						this.calculateFullSubscriber(response, car);
						break;
					case ONE_TIME_USE:
						this.calculateOneTimeUse(request, response, car, site, useHours, subscription);
						break;
					case ORDER_AHEAD:
						this.calculateOrderAhead(request, response, car, site, useHours, subscription);
						break;
				}

				this.sendResponseRefresh(site, client);
			}
		}

		if (!success)
		{
			String extraData = String.format(UserNotification.THIS_CARID_DOSENT_FOUND, request.getCarID());
			response.setErrorMessage(extraData);
		}
		this.sendResponse(response, client);
	}

	private void calculateFullSubscriber(ResponseRequest response, Car car)
	{
		DateTime daysCount = car.getArrivedTime().plusDays(14);

		if (!SystemClock.getClockInstance().isDateBeforeToday(daysCount))
		{
			String extraData = String.format(UserNotification.YOU_PARKED_MORE_THAT_14_DAYS);
			response.setErrorMessage(extraData);
		}
		else
		{
			String extraData = UserNotification.SUBSCRIBEL_PARKING_DONE;
			response.setData(extraData);
		}
		this.getDaoFactory().getCarDAO().updateParkedOnSite(car.getCarID(), false);
	}

	private void calculateCasual(ResponseRequest response, Car car, DateTime currentTime)
	{
		this.getDaoFactory().getCarDAO().updateLastCheckOutDate(car.getCarID(), currentTime);
		this.getDaoFactory().getCarDAO().updateParkedOnSite(car.getCarID(), false);
		String extraData = UserNotification.SUBSCRIBEL_PARKING_DONE;
		response.setData(extraData);
	}

	private void calculateOrderAhead(CustomerCheckoutRequest request, ResponseRequest response, Car car, Site site, int useHours, Subscription subscription)
	{
		Customer customer = this.getDaoFactory().getCustomerDAO().findByID(car.getCustomerID());

		if (!this.CopySubscription(subscription, request.getSite(), car))
		{
			String extraData = String.format(UserNotification.SUBSCRIBEL_REMOVE_ERROR);
			response.setErrorMessage(extraData);
		}
		else
		{
			int payForHours = this.calculateHours(car, subscription.getEstimatedLeaveDate());

			int lateArrivalMinutes = Minutes.minutesBetween(subscription.getArrivalDate(), car.getArrivedTime()).getMinutes();
			if (lateArrivalMinutes > 0)
			{
				lateArrivalMinutes = (lateArrivalMinutes / 10) + 1;
				payForHours = payForHours + lateArrivalMinutes;
			}

			if (useHours == payForHours)
			{
				String extraData = String.format(UserNotification.PAYMENT_DONE);
				response.setData(extraData);
				customer.setBalance(customer.getBalance() - (payForHours * site.getOrderAheadPricePerHour()));
			}
			else if (useHours > payForHours)
			{
				float toRefound = (site.getOrderAheadPricePerHour() * useHours) - (site.getOrderAheadPricePerHour() * payForHours);
				String extraData = String.format(UserNotification.PAYMENT_REFUND, toRefound);
				response.setData(extraData);
				customer.setBalance(customer.getBalance() + ((useHours - payForHours) * site.getOrderAheadPricePerHour()));
			}
			else
			{
				float toPay = (payForHours - useHours) * site.getSingleUsePricePerHour();
				String extraData = String.format(UserNotification.PAYMENT_PAY__Bill__, toPay);
				response.setData(extraData);
				customer.setBalance(customer.getBalance() - (toPay + (useHours * site.getOrderAheadPricePerHour())));
			}

			this.getDaoFactory().getCarDAO().deleteByID(request.getCarID());
			this.getDaoFactory().getSubscriptionDAO().deleteByID(subscription.getSubscribtionID());
			customer.setBussinessClass(customer.getBussinessClass() - 1);
			this.getDaoFactory().getCustomerDAO().updateBussinessClass(customer.getCustomerID(), customer.getBussinessClass());
		}
	}

	private void calculateOneTimeUse(CustomerCheckoutRequest request, ResponseRequest response, Car car, Site site, int useHours, Subscription subscription)
	{
		// move to subscriptionHistory
		if (!this.CopySubscription(subscription, request.getSite(), car))
		{
			String extraData = String.format(UserNotification.SUBSCRIBEL_REMOVE_ERROR);
			response.setErrorMessage(extraData);
		}
		else
		{
			float toPay = site.getSingleUsePricePerHour() * useHours;
			String extraData = String.format(UserNotification.CAR_RELEASED_ONE_TIME_USE, car.getCarID(), toPay);
			response.setData(extraData);
			// Remove tables lines and update site status
			this.getDaoFactory().getCarDAO().deleteByID(request.getCarID());
			this.getDaoFactory().getSubscriptionDAO().deleteByID(subscription.getSubscribtionID());
			Customer customer = this.getDaoFactory().getCustomerDAO().findByID(car.getCustomerID());
			if (customer.getBussinessClass() == 1)
			{
				this.getDaoFactory().getCustomerDAO().deleteByID(car.getCustomerID());
				this.getDaoFactory().getUserDAO().deleteByID(car.getCustomerID());
			}
			else
			{
				customer.setBussinessClass(customer.getBussinessClass() - 1);
			}
		}
	}

	private int calculateHours(Car car, DateTime time)
	{
		int hours = Minutes.minutesBetween(car.getArrivedTime(), time).getMinutes();
		hours = (hours / 60) + 1;

		return hours;
	}

	private Site rebuildSiteParkingMatrix(CustomerCheckoutRequest request, Lot lot)
	{
		this.getDaoFactory().getLotDAO().updateCarInLot(request.getSite().getSiteID(), lot.getFloor(), lot.getRow(), lot.getColumn(), null);
		this.getDaoFactory().getLotDAO().updateLotStatusByID(request.getSite().getSiteID(), lot.getFloor(), lot.getRow(), lot.getColumn(), LotStatus.FREE);

		Robot robot = RobotManager.getRobotManagerInstance().getSiteRobot(request.getSite());
		robot.removeCar();

		Site site = this.getDaoFactory().getSiteDAO().findByID(request.getSite().getSiteID());
		this.getDaoFactory().getSiteDAO().getSiteLotsOnDemand(site);
		return site;
	}

	private void removeOccupiedLotFromSite(Lot lot, Site site)
	{
		int occupied = site.getOccupiedLots();
		occupied--;
		this.getDaoFactory().getSiteDAO().updateOccupiedLotsByID(lot.getParentSite().getSiteID(), occupied);
		site.setOccupiedLots(occupied);
	}

	private boolean isCarIDNotRegisteredToSite(CustomerCheckoutRequest request, Lot lot, Site site)
	{
		return (lot != null) && (site.getSite() == request.getSite());
	}

	private boolean isCarIDCorrect(Car car)
	{
		return car != null;
	}

	/**
	 * archives order ahead and one time use customer data
	 * 
	 * @param subscription
	 * @param site
	 * @return true if data was saved
	 */
	public boolean CopySubscription(Subscription subscription, SiteEnum site, Car car)
	{
		SubscriptionHistory subscriptionHistory = this.getDaoFactory().getSubscriptionHistoryDAO().create();
		subscriptionHistory.setArrivalDate(car.getArrivedTime());
		subscriptionHistory.setEndDate(subscription.getEndDate());
		subscriptionHistory.setLeaveDate(SystemClock.getClockInstance().getSystemDateTime());
		subscriptionHistory.setSite(site);
		subscriptionHistory.setStartDate(subscription.getArrivalDate());
		subscriptionHistory.setSubscriptionID(subscription.getSubscribtionID());
		subscriptionHistory.setSubscriptionType(subscription.getSubscriptionType());
		subscriptionHistory.setCustomerID(subscription.getCustomerID());
		subscriptionHistory.setCanceld(false);

		boolean respond = this.getDaoFactory().getSubscriptionHistoryDAO().save(subscriptionHistory);
		return respond;
	}

}
